<?php
/**
 * Xans 확장팩 - JS, CSS 옵티마이져
 *
 * !!WARNING!!
 * 싱글톤 Optimizer 인스턴스를 활용하므로 프레임워크의 옵티마이져가 초기화된 이후에 정상적인 사용이 가능함.
 * 컴파일모드를 지원하기 위해 직접 인스턴스를 할당받도록 함.
 *
 * @package     util
 * @subpackage  xans
 * @author      Platform Team
 */

class utilXansExtensionOptimizer
{
    const KEY_FILE_NOT_EXIST = 0;
    const KEY_FILE_EXIST = 1;

    /**
     * DOCROOT 리스트
     * @var array(
     *  '절대경로1', '절대경로2', ...
     * )
     */
    private $aDocRoot = array();

    /**
     * JS 리스트
     * @var array(
     *  '현재디렉토리' => array(
     *       '인덱스' => array(
     *           'path' => 절대경로(존재하지 않는 경우는 false)
     *           'simple_path' => 전처리구문에 작성된 경로(/js/test1.js 와 같은 경로)
     *           'exist' => 파일존재여부(1 : 존재, 0 : 존재하지 않는 파일)
     *           'is_parent' => 부모 경로에 매칭된 경로인가(true or false)
     *          'is_absolute_path' => 전처리구문에 작성된 경로가 /로 시작하는 절대경로인지 여부(true or false)
     *          'uid' => 매칭된 루트 디렉토리 인덱스
     *       ),
     *       ...
     *   ),
     *   ...
     * )
     */
    private $aJs = array();

    /**
     * CSS 리스트
     * @var array(
     *  '현재디렉토리' => array(
     *       '인덱스' => array(
     *           'path' => 절대경로(존재하지 않는 경우는 false)
     *           'simple_path' => 전처리구문에 작성된 경로(/js/test1.js 와 같은 경로)
     *           'exist' => 파일존재여부(1 : 존재, 0 : 존재하지 않는 파일)
     *           'is_parent' => 부모 경로에 매칭된 경로인가(true or false)
     *          'is_absolute_path' => 전처리구문에 작성된 경로가 /로 시작하는 절대경로인지 여부(true or false)
     *          'uid' => 매칭된 루트 디렉토리 인덱스
     *       ),
     *       ...
     *   ),
     *   ...
     * )
     */
    private $aCss = array();

    /**
     * 실행 - 갱신시 실행
     * XANS_EXT_EACH_FILE_READ
     *
     * @param string &$sSource
     * @param string $sCurrDir
     * @param $Reader
     */
    public function parse(&$sSource, $sCurrDir, $Reader)
    {
        $Optimizer = Optimizer::instance();

        if (!$Optimizer) return;

        $this->aDocRoot = $Reader->getDocRoot();

        $this->_replaceOptimizerJs($sSource, $sCurrDir, $Reader, $Optimizer);
        $this->_replaceOptimizerCss($sSource, $sCurrDir, $Reader, $Optimizer);
    }

    /**
     * 실행 - 캐시생성 이후 실행
     * XANS_EXT_EACH_FILE_READ
     */
    public function parseCache()
    {
        $Optimizer = Optimizer::instance();

        if (!$Optimizer) return;

        if (!empty($this->aJs)) {
            foreach ($this->aJs as $sCurrDir => $aList) {

                foreach ($aList as $iIdx => $aItem) {
                    if ($aItem['exist'] == self::KEY_FILE_EXIST) {
                        if ($aItem['is_parent'] === true && $aItem['is_absolute_path'] === true) {
                            $p = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path']);
                            $Optimizer->addJS('', $p['full'], 'after', false);
                        } else {
                            $isAdd = $Optimizer->addJS('', $aItem['path'], 'after', false);

                            if ($isAdd === false) {
                                $p = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path']);
                                $Optimizer->addJS('', $p['full'], 'after', false);
                            }
                        }
                    }


                    if ($aItem['exist'] == self::KEY_FILE_NOT_EXIST) {
                        $aPathInfo = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path'], $sCurrDir);
                        $Optimizer->addJS('', $aPathInfo['full'], 'after', false);
                    }
                }

            }
        }

        if (!empty($this->aCss)) {
            foreach ($this->aCss as $sCurrDir => $aList) {

                foreach ($aList as $iIdx => $aItem) {
                    if ($aItem['exist'] == self::KEY_FILE_EXIST) {
                        if ($aItem['is_parent'] === true && $aItem['is_absolute_path'] === true) {
                            $p = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path']);
                            $Optimizer->addCSS('', $p['full'], 'after', false);
                        } else {
                            $isAdd = $Optimizer->addCSS('', $aItem['path'], 'after', false);

                            if ($isAdd === false) {
                                $p = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path']);
                                $Optimizer->addJS('', $p['full'], 'after', false);
                            }
                        }
                    }


                    if ($aItem['exist'] == self::KEY_FILE_NOT_EXIST) {
                        $aPathInfo = xansGetAbsolutePath($this->aDocRoot, $aItem['simple_path'], $sCurrDir);
                        $Optimizer->addCSS('', $aPathInfo['full'], 'after', false);
                    }
                }

            }
        }
    }

    /**
     * 실행 - 항상 실행
     * XANS_EXT_PRE_COMPLETE
     */
    public function makeOptimizerTag($Xans)
    {
        $Optimizer = Optimizer::instance();

        $this->_makeOptimizerJs($Xans, $Optimizer);
        $this->_makeOptimizerCss($Xans, $Optimizer);
    }

    /**
     * 최적화 JS 태그 삽입
     *
     * @param Xans $Xans
     * @param Optimizer $Optimizer
     */
    private function _makeOptimizerJs($Xans, $Optimizer)
    {
        if (!$Optimizer) return;

        $sTag = $Optimizer->makeOptimizerJsTag();

        if (FW_XANS_SCRIPT_TAG_POSITION_BOTTOM === false) {
            $Xans->headTail($sTag);
        } else {
            $Xans->bodyTail($sTag);
        }
    }

    /**
     * 최적화 CSS 태그 삽입
     *
     * @param Xans $Xans
     * @param Optimizer $Optimizer
     */
    private function _makeOptimizerCss($Xans, $Optimizer)
    {
        if (!$Optimizer) return;

        $sTag = $Optimizer->makeOptimizerCssTag();

        $Xans->headTail($sTag);
    }


    /**
     * JS 옵티마이져 구문 치환
     *
     * @param string &$sSource
     * @param string $sCurrDir
     * @param Xans_Reader $Reader
     * @param Optimizer $Optimizer
     */
    private function _replaceOptimizerJs(&$sSource, $sCurrDir, $Reader, $Optimizer)
    {
        $sRegex = '/<!--@js\((.+?)\)-->/i';

        if (preg_match_all($sRegex, $sSource, $aMatch) === 0) return;

        foreach ($aMatch[1] as $m) {
            $aPathInfo = $Reader->getAbsolutePath($m, $sCurrDir);

            $sFullPath = $aPathInfo['full'];

            if ($aPathInfo === false) {
                $this->aJs[$sCurrDir][] = array(
                    'path' => false,
                    'simple_path' => $m,
                    'exist' => self::KEY_FILE_NOT_EXIST,
                    'is_parent' => false,
                    'is_absolute_path' => false,
                    'uid' => false
                );
                continue;
            }

            $this->aJs[$sCurrDir][] = array(
                'path' => $sFullPath,
                'simple_path' => $m,
                'exist' => self::KEY_FILE_EXIST,
                'is_parent' => $aPathInfo['is_parent'],
                'is_absolute_path' => $aPathInfo['is_absolute_path'],
                'uid' => $aPathInfo['uid']
            );

            $Optimizer->addJS('', $sFullPath, 'after', false);
        }

        $sSource = preg_replace($sRegex, '', $sSource);
    }

    /**
     * CSS 옵티마이져 구문 치환
     *
     * @param string &$sSource
     * @param string $sCurrDir
     * @param Xans_Reader $Reader
     * @param Optimizer $Optimizer
     */
    private function _replaceOptimizerCss(&$sSource, $sCurrDir, $Reader, $Optimizer)
    {
        $sRegex = '/<!--@css\((.+?)\)-->/i';

        if (preg_match_all($sRegex, $sSource, $aMatch) === 0) return;

        foreach ($aMatch[1] as $m) {
            $aPathInfo = $Reader->getAbsolutePath($m, $sCurrDir);

            $sFullPath = $aPathInfo['full'];

            if ($aPathInfo === false) {
                $this->aCss[$sCurrDir][] = array(
                    'path' => false,
                    'simple_path' => $m,
                    'exist' => self::KEY_FILE_NOT_EXIST,
                    'is_parent' => false,
                    'is_absolute_path' => false,
                    'uid' => false
                );
                continue;
            }

            $this->aCss[$sCurrDir][] = array(
                'path' => $sFullPath,
                'simple_path' => $m,
                'exist' => self::KEY_FILE_EXIST,
                'is_parent' => $aPathInfo['is_parent'],
                'is_absolute_path' => $aPathInfo['is_absolute_path'],
                'uid' => $aPathInfo['uid']
            );

            $Optimizer->addCSS('', $sFullPath, 'after', false);
        }

        $sSource = preg_replace($sRegex, '', $sSource);
    }
}
